如何用borb在Python中提取和处理PDF发票

您所在的位置:网站首页 python 获取pdf页数 如何用borb在Python中提取和处理PDF发票

如何用borb在Python中提取和处理PDF发票

2023-08-18 19:30| 来源: 网络整理| 查看: 265

简介

该 ***可移植文档格式(PDF)***并不是一种 ***所见即所得(WYSIWYG)。***格式。它被开发成与平台无关,与底层操作系统和渲染引擎无关。

为了实现这一点,PDF的构造是通过更像编程语言的东西进行交互,并依靠一系列的指令和操作来实现结果。事实上,PDF是基于一种脚本语言--PostScript,它是第一个独立于设备的页面描述语言。

在本指南中,我们将使用 borb- 一个专门用于阅读、操作和生成 PDF 文档的 Python 库。它提供了一个低级别的模型(如果你选择使用精确的坐标和布局,允许你访问这些)和一个高级别的模型(你可以将边距、位置等的精确计算委托给一个布局管理器)。

在本指南中,我们将看看如何用borb在Python中处理一张PDF发票,通过提取文本,因为PDF是一种可提取的格式--这使得它容易被自动化处理。

自动处理是机器的基本目标之一,如果有人不提供可解析的文档,比如json ,同时提供面向人类的发票--你就必须自己解析PDF内容。

安装borb

borb可以从GitHub上的源代码下载,或者通过pip 。

$ pip install borb

**注意:**如果你在 Windows 上工作,你可能需要安装一个额外的依赖。

$ pip install windows-curses 用 borb 在 Python 中创建 PDF 发票

在前面的指南中,我们已经用 borb 生成了一张 PDF 发票,现在我们将对其进行处理。

生成的PDF文档具体看起来是这样的。

borb invoice 5

用borb处理一个PDF发票

让我们从打开PDF文件开始,并将其加载到Document - 文件的对象表示。

import typing from borb.pdf.document import Document from borb.pdf.pdf import PDF def main(): d: typing.Optional[Document] = None with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle) assert d is not None if __name__ == "__main__": main()

该代码遵循你可能在json 库中看到的相同模式;一个静态方法,loads() ,它接受一个文件手柄,并输出一个数据结构。

接下来,我们希望能够提取文件的所有文本内容。borb ,允许你将EventListener 类注册到Document 的解析中,从而实现这一目标。

例如,每当borb 遇到某种文本渲染指令,它就会通知所有注册的EventListener 对象,然后这些对象就可以处理发出的Event 。

borb EventListener 的相当多的实现。

SimpleTextExtraction: 从PDF中提取文本 SimpleImageExtraction:从PDF中提取所有图像 RegularExpressionTextExtraction:匹配一个正则表达式,并返回每页的匹配结果 等等。

我们将从提取所有文本开始。

import typing from borb.pdf.document import Document from borb.pdf.pdf import PDF # New import from borb.toolkit.text.simple_text_extraction import SimpleTextExtraction def main(): d: typing.Optional[Document] = None l: SimpleTextExtraction = SimpleTextExtraction() with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle, [l]) assert d is not None print(l.get_text_for_page(0)) if __name__ == "__main__": main()

这个代码片段应该按照阅读顺序(从上到下,从左到右)打印发票中的所有文本。

[Street Address] Date 6/5/2021 [City, State, ZIP Code] Invoice # 1741 [Phone] Due Date 6/5/2021 [Email Address] [Company Website] BILL TO SHIP TO [Recipient Name] [Recipient Name] [Company Name] [Company Name] [Street Address] [Street Address] [City, State, ZIP Code] [City, State, ZIP Code] [Phone] [Phone] DESCRIPTION QTY UNIT PRICE AMOUNT Product 1 2 $ 50 $ 100 Product 2 4 $ 60 $ 240 Labor 14 $ 60 $ 840 Subtotal $ 1,180.00 Discounts $ 177.00 Taxes $ 100.30 Total $ 1163.30

当然,这对我们来说不是很有用,因为这需要更多的处理,然后才能做很多事情,虽然这是一个很好的开始,特别是与OCR扫描的PDF文件相比

让我们细化这段代码,告诉borb ,我们对哪些Rectangle 。

例如,让我们提取运输信息(但你可以修改代码以检索任何感兴趣的领域)。

为了让borb ,以过滤出一个Rectangle ,我们将使用LocationFilter 类。这个类实现了EventListener 。它在渲染Page 时得到所有Events 的通知,并将那些发生在预先定义的边界内的(给它的子类)传递出去。

import typing from decimal import Decimal from borb.pdf.document import Document from borb.pdf.pdf import PDF from borb.toolkit.text.simple_text_extraction import SimpleTextExtraction # New import from borb.toolkit.location.location_filter import LocationFilter from borb.pdf.canvas.geometry.rectangle import Rectangle def main(): d: typing.Optional[Document] = None # Define rectangle of interest # x, y, width, height r: Rectangle = Rectangle(Decimal(280), Decimal(510), Decimal(200), Decimal(130)) # Set up EventListener(s) l0: LocationFilter = LocationFilter(r) l1: SimpleTextExtraction = SimpleTextExtraction() l0.add_listener(l1) with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle, [l0]) assert d is not None print(l1.get_text_for_page(0)) if __name__ == "__main__": main()

运行这段代码,假设选择了正确的矩形,就会打印出来。

SHIP TO [Recipient Name] [Company Name] [Street Address] [City, State, ZIP Code] [Phone]

这段代码并不完全是最灵活或最适合未来的。要找到正确的Rectangle ,需要花些功夫,而且不能保证在发票的布局稍有变化时它也能工作。

我们将不得不建立一些更强大的东西,以获得实际的应用。

我们可以从删除硬编码的Rectangle 开始。RegularExpressionTextExtraction 可以匹配正则表达式,并返回(除其他外)它在Page 上的坐标!使用模式匹配,我们可以自动搜索文档中的元素并检索它们,而不是猜测在哪里画一个矩形。

让我们用这个类来寻找 "SHIP TO "这个词,并根据这些坐标建立一个Rectangle 。

import typing from borb.pdf.document import Document from borb.pdf.pdf import PDF from borb.pdf.canvas.geometry.rectangle import Rectangle # New imports from borb.toolkit.text.regular_expression_text_extraction import RegularExpressionTextExtraction, PDFMatch def main(): d: typing.Optional[Document] = None # Set up EventListener l: RegularExpressionTextExtraction = RegularExpressionTextExtraction("SHIP TO") with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle, [l]) assert d is not None matches: typing.List[PDFMatch] = l.get_matches_for_page(0) assert len(matches) == 1 r: Rectangle = matches[0].get_bounding_boxes()[0] print("%f %f %f %f" % (r.get_x(), r.get_y(), r.get_width(), r.get_height())) if __name__ == "__main__": main()

在这里,我们围绕该部分建立了一个Rectangle ,并打印了它的坐标。

299.500000 621.000000 48.012000 8.616000

你会注意到,get_bounding_boxes() ,返回typing.List[Rectangle] 。当一个正则表达式在PDF中的多行文本中被匹配时,就是这种情况。

另外,请记住,PDF的原点([0, 0] 点)位于左下角。因此,Page 的顶部有最高的 Y 坐标。

现在我们知道在哪里可以找到*"SHIP TO"*,我们可以更新我们先前的代码,将感兴趣的Rectangle ,就在这些字的下面。

import typing from decimal import Decimal from borb.pdf.document import Document from borb.pdf.pdf import PDF from borb.pdf.canvas.geometry.rectangle import Rectangle from borb.toolkit.location.location_filter import LocationFilter from borb.toolkit.text.regular_expression_text_extraction import RegularExpressionTextExtraction, PDFMatch from borb.toolkit.text.simple_text_extraction import SimpleTextExtraction def find_ship_to() -> Rectangle: d: typing.Optional[Document] = None # Set up EventListener l: RegularExpressionTextExtraction = RegularExpressionTextExtraction("SHIP TO") with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle, [l]) assert d is not None matches: typing.List[PDFMatch] = l.get_matches_for_page(0) assert len(matches) == 1 return matches[0].get_bounding_boxes()[0] def main(): d: typing.Optional[Document] = None # Define rectangle of interest ship_to_rectangle: Rectangle = find_ship_to() r: Rectangle = Rectangle(ship_to_rectangle.get_x() - Decimal(50), ship_to_rectangle.get_y() - Decimal(100), Decimal(200), Decimal(130)) # Set up EventListener(s) l0: LocationFilter = LocationFilter(r) l1: SimpleTextExtraction = SimpleTextExtraction() l0.add_listener(l1) with open("output.pdf", "rb") as pdf_in_handle: d = PDF.loads(pdf_in_handle, [l0]) assert d is not None print(l1.get_text_for_page(0)) if __name__ == "__main__": main()

然后这段代码就打印出来了。

SHIP TO [Recipient Name] [Company Name] [Street Address] [City, State, ZIP Code] [Phone]

这仍然需要对文档有一定的了解,但并不像之前的方法那样死板--只要你知道你想提取哪段文字--你就可以得到坐标,并在页面上的一个矩形范围内攫取内容。

总结

在本指南中,我们已经看了如何使用borb在Python中处理一张发票。我们从提取所有的文本开始,然后改进我们的过程,只提取一个感兴趣的区域。最后,我们用正则表达式与 PDF 匹配,使这一过程更加稳健,更适合未来的发展。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3